package app.sunstreak.yourpisd.net; import org.jsoup.Connection; import org.jsoup.HttpStatusException; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.FormElement; import java.io.IOException; import java.net.SocketTimeoutException; import java.net.URL; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import app.sunstreak.yourpisd.net.data.Student; public class Session { public static final String GRADEBOOK_ROOT = "https://gradebook.pisd.edu/Pinnacle/Gradebook"; public static final String LOGOFF = GRADEBOOK_ROOT + "/Logon.aspx?Action=Logout"; public static final String LOGON = GRADEBOOK_ROOT + "/logon.aspx"; // Example: Sat Sep 03 2016 20:31:32 GMT-0500 (Central Daylight Time) public static final SimpleDateFormat FULL_DATE_FORMAT = new SimpleDateFormat("EEE MMM dd yyyy H:mm:ss 'GMT'z", Locale.ENGLISH); private final String username; private final String password; private boolean loggedIn = false; private final Map<String, String> cookies = new HashMap<>(); private Date expiration; List<Student> students = new ArrayList<>(); public int studentIndex = 0; public boolean MULTIPLE_STUDENTS; public static Session createSession(String username, String password) { return new Session(username, password); } private Session(String username, String password) { this.username = username; this.password = password; } public String getUsername() { return username; } public String getPassword() { return password; } /** * Loads a GET request from a specified path and query info * @param path the path (beginning with a forward slash). * @param params parameter information to pass as query. * @return the body string, or null if error occurs. * @throws IOException */ public String request(String path, Map<String, String> params) throws IOException { if (checkExpiration() != 1) return null; Connection conn = Jsoup.connect(Session.GRADEBOOK_ROOT + path) .timeout(60000).cookies(getCookies()); if (params != null) conn.data(params); try { Connection.Response resp = conn.execute(); return resp.body(); } catch (HttpStatusException e) { e.printStackTrace(); return null; } } public List<Student> getStudents() { return students; } public Map<String, String> getCookies() { return cookies; } public Student getCurrentStudent() { return students.get(studentIndex); } /** * Checks the expiration of the current session * @return the status code (1 for good, -1 for bad password, -2 for server error). * @throws IOException */ public int checkExpiration() throws IOException { if (Jsoup.connect(GRADEBOOK_ROOT + "/InternetViewer/GradeSummary.aspx").cookies(cookies) .timeout(60000).execute().url().toExternalForm().equalsIgnoreCase(LOGON)) { int status = login(); if (status <= 0) return status; } return 1; } /** * This function logs in the student/ parent with their current credentials to Gradebook Pinnacle. * It then records session IDs and expiration time via cookies. * Precondition: username and password are both defined. * * @return a status code (1 for no error, -1 for bad password, and -2 for server error.) * @throws IOException if an I/O error occurs while connecting to server. */ public int login() throws IOException { final URL LOAD_URL = new URL(LOGON); final String USERNAME_FIELD = "ctl00$ContentPlaceHolder$Username"; final String PASSWORD_FIELD = "ctl00$ContentPlaceHolder$Password"; try { // Submitting form data. Document html = Jsoup.parse(LOAD_URL, 60000); FormElement form = (FormElement) html.getElementsByTag("form").get(0); Connection conn = form.submit(); // Find username and password field. Connection.KeyVal usernameField = null; Connection.KeyVal passwordField = null; for (Connection.KeyVal field : conn.request().data()) if (field.key().equals(USERNAME_FIELD)) usernameField = field; else if (field.key().equals(PASSWORD_FIELD)) passwordField = field; if (usernameField == null) { System.err.println("EXPECTED: Form field for username."); return -2; } if (passwordField == null) { System.err.println("EXPECTED: Form field for password."); return -2; } // Submit username and password usernameField.value(username); passwordField.value(password); Connection.Response resp = conn.timeout(60000).execute(); if (resp.url().equals(LOAD_URL)) return -1; else { cookies.putAll(resp.cookies()); updateExpirationDate(); students.addAll(Parser.parseStudents(this, resp.body())); MULTIPLE_STUDENTS = students.size() > 1; loggedIn = true; return 1; } } catch (SocketTimeoutException e) { e.printStackTrace(); return -2; } } /** * Updates expiration dates for the current session. */ public void updateExpirationDate() { try { if (cookies.containsKey("SessionReminder")) expiration = FULL_DATE_FORMAT.parse(cookies.get("SessionReminder")); } catch (ParseException e) { System.err.println("Unable to parse expiration time-stamp"); e.printStackTrace(); } } /** * Logs out the user from their account, and resets cookies. * @return true if we successfully logged out, false for an error. */ public boolean logout() { try { boolean success = Jsoup.connect(LOGOFF).timeout(60000).cookies(cookies).execute() .statusCode() == 200; cookies.clear(); return success; } catch (IOException e) { return false; } } }